#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include <errno.h>
//第16讲：字符函数和字符串函数
//对strcat的补充:
//字符串⾃⼰给⾃⼰追加，如何？
//使用库函数的可以，但是用自己做的strcat就有问题:
//char* my_strcat(char* p1, const char* p2)
//{
//	assert(p1 && p2);
//	char* ret = p1;
//	while (*p1)
//		p1++;
//	while (*p1++ = *p2++)
//		;
//	return ret;
//}
//int main()
//{
//	char arr[] = "abcdef";
//	my_strcat(arr, arr);//错误，原因是当p1走到\0的时候，把*p2赋给*p1后\0就被覆盖掉了，程序陷入死循环
//	return 0;
//}


//6. strcmp 的使⽤和模拟实现
//int strcmp ( const char * str1, const char * str2 );
//一个字符一个字符的比较
// > ---> return 大于0的数字
// < ---> return 小于0的数字
// = ---> return 0
//在vs上返回的值是固定的，分别是1 -1 0但在其他编译器上返回什么就不知道了
//在判断的时候判断大于0还是小于0，而不是判断是否等于1或者-1，这是不准确的

//strcmp 模拟实现
//int my_strcmp(const char* str1, const char* str2)
//{
//	assert(str1 && str2);
//	while (*str1 == *str2)
//	{
//		if (*str1 == '\0')
//			return 0;
//		str1++;
//		str2++;
//	}
//	//if (*str1 > *str2)
//	//	return 1;
//	//else
//	//	return -1;
//	//上面被注释的代码优化后：
//	return *str1 - *str2;
//}
//int main()
//{
//	char arr1[] = "abcde";
//	char arr2[] = "abcdefg";
//	int ret = my_strcmp(arr1, arr2);
//	printf("%d\n", ret);
//	return 0;
//}


//长度不受限制的字符串函数:strcpy strcat strcmp
//长度受限制的字符串函数:strncpy strncat strncmp
//7. strncpy 函数的使⽤
//char * strncpy ( char * destination, const char * source, size_t num );
//• 拷⻉num个字符从源字符串到⽬标空间。
//• 如果源字符串的⻓度⼩于num，则拷⻉完源字符串之后，在⽬标的后边追加0，直到num个。
//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[] = "xxxxxxxxxxxxxxxxxxxxx";
//	strncpy(arr2, arr1, 3);
//	printf("%s", arr2);//只拷贝arr1中三个字符并且后面不加\0;
//	return 0;
//}

//strncpy的模拟实现:
//char* my_strncpy(char* destination, const char* source, size_t num)
//{
//	assert(destination && source);
//	int count = 0;
//	for (int i = 0; *(destination + i) != '\0'; i++)
//	{
//		count++;
//	}
//	if (num <= count)
//	{
//		for (int i = 0; i < num; i++)
//		{
//			*(destination + i) = *(source + i);
//		}
//	}
//	else
//	{
//		int i = 0;
//		for (i = 0; i < num; i++)
//		{
//			*(destination + i) = *(source + i);
//		}
//		for (; i < count; i++)
//		{
//			*(destination + i) = '\0';
//		}
//	}
//	return destination;
//}
//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[] = "xxxxxxxxxxxxxxxxxxxxx";
//	my_strncpy(arr2, arr1, 9);
//	printf("%s", arr2);
//	return 0;
//}
//

//8. strncat 函数的使⽤
//char * strncat ( char * destination, const char * source, size_t num );
//（将source指向字符串的前num个字符追加到destination指向的字符串末尾，再追加⼀个 \0 字符）。
//（如果source 指向的字符串的⻓度⼩于num的时候，只会将字符串中到\0 的内容追加到destination指向的字符串末尾）。
//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[20] = "xxx";//因为要在arr2后面追加字符，所以[]里要填入一个较大的值来腾出空间
//	strncat(arr2, arr1, 2);
//	printf("%s\n", arr2);
//	return 0;
//}

//9. strncmp函数的使⽤
//int strncmp ( const char * str1, const char * str2, size_t num );
//⽐较str1和str2的前num个字符，如果相等就继续往后⽐较，最多⽐较num个字⺟，如果提前发现不⼀
//样，就提前结束，⼤的字符所在的字符串⼤于另外⼀个。如果num个字符都相等，就是相等返回0.



//长度不受限制的字符串函数:strcpy strcat strcmp
					//这些函数是不安全的

//长度受限制的字符串函数:strncpy strncat strncmp
					//这些函数是相对安全的
//int main()
//{
//	char arr1[10] = "abcdef";
//	char arr2[] = "abcdefghijklmn";
//	strcpy(arr1, arr2);//把arr2里的内容全部拷贝到arr1里，不管arr1能不能放得下，会造成越界访问。
//	return 0;		   //所以说长度不受限制的字符串函数是不安全的.
//}


//10. strstr 的使⽤和模拟实现
//char * strstr ( const char * str1, const char * str2);
//函数返回字符串str2在字符串str1中第⼀次出现的位置
//字符串的⽐较匹配不包含 \0 字符，以 \0 作为结束标志
//int main()
//{
//	char arr1[] = "this is an apple";
//	char arr2[] = "is";
//	char* ret=strstr(arr1, arr2);
//	printf("%s\n", ret);
//	return 0;
//}

//strstr的模拟实现：
//char* my_strstr(const char* str1, const char* str2)
//{
//	const char* cur = str1;
//	const char* s1 = str1;
//	const char* s2 = str2;
//	while (*cur)
//	{
//		s1 = cur;
//		s2 = str2;
//		while (*s1!='\0'&& *s2 != '\0'&& * s1++ == *s2++)
//			;
//		if (*s2 == '\0')
//			return (char*)cur;
//		cur++;
//	}
//	return (NULL);
//}
//int main()
//{
//	char arr1[] = "this is an apple";
//	char arr2[] = "is";
//	char*ret = my_strstr(arr1, arr2);
//	printf("%s", ret);
//	return 0;
//}


//11. strtok 函数的使⽤
//char * strtok ( char * str, const char * sep);
//• sep参数指向⼀个字符串，定义了⽤作分隔符的字符集合
//• 第⼀个参数指定⼀个字符串，它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。
//• strtok函数找到str中的下⼀个标记，并将其⽤ \0 结尾，返回⼀个指向这个标记的指针。
//（注：strtok函数会改变被操作的字符串，所以被strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。）
//• strtok函数的第⼀个参数不为 NULL ，函数将找到str中第⼀个标记，strtok函数将保存它在字符串中的位置。
//• strtok函数的第⼀个参数为 NULL ，函数将在同⼀个字符串中被保存的位置开始，查找下⼀个标记。
//• 如果字符串中不存在更多的标记，则返回 NULL 指针。
//int main()
//{
//	char arr1[] = "abc.def@ghi";
//	char arr2[20];
//	char arr3[] = "@.";
//	strcpy(arr2, arr1);
//	for (char* ret = strtok(arr2, arr3); ret != NULL;ret=strtok(NULL,arr3))
//	{
//		printf("%s\n", ret);
//	}
//	return 0;
//}


//12. strerror 函数的使⽤
//char* strerror ( int errnum );
//strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
//在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码，⼀般是放在 errno.h 这个头⽂件中说明
//的，C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码，只不过程序启动
//的时候errno是0，表⽰没有错误，当我们在使⽤标准库中的函数的时候发⽣了某种错误，就会将对应
//的错误码，存放在errno中，⽽⼀个错误码的数字是整数很难理解是什么意思，所以每⼀个错误码都是
//有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
//int main()
//{
//	int i = 0;
//	for (i = 0; i <= 10; i++) {
//		printf("%d:\t%s\n",i, strerror(i));
//	}
//	return 0;
//}

//举例：
//int main()
//{
//	FILE* pFile;
//	pFile = fopen("unexist.ent", "r");//以读的形式打开unexist.ent这个文件
//	if (pFile == NULL)//如果是空指针（代表没有这个文件）
//		printf("Error opening file unexist.ent: %s\n", strerror(errno));//就打印错误码对应的错误信息
//	return 0;
//}

//也可以了解⼀下 perror 函数，perror
//函数相当于⼀次将上述代码中的第9⾏完成了，直接将错误信息
//打印出来。perror函数打印完参数部分的字符串后，再打印⼀个冒号和⼀个空格，再打印错误信息。
//int main()
//{
//	FILE * pFile;
//	pFile = fopen("unexist.ent", "r");
//	if (pFile == NULL)
//		perror("Error opening file unexist.ent");//相当于printf(str)+printf(strerror(errno))
//	return 0;
//}
















